package cn.topcodes.tcsf.app.service.impl;

import cn.topcode.unicorn.utils.*;
import cn.topcodes.tcsf.admin.commons.domain.Page;
import cn.topcodes.tcsf.admin.commons.generic.GenericService;
import cn.topcodes.tcsf.app.domain.entity.*;
import cn.topcodes.tcsf.app.domain.form.AppClientInfo;
import cn.topcodes.tcsf.app.domain.report.RespAppUserInfo;
import cn.topcodes.tcsf.app.domain.report.UserRetentionVo;
import cn.topcodes.tcsf.app.event.AppUserLoginEvent;
import cn.topcodes.tcsf.app.event.AppUserLogoutEvent;
import cn.topcodes.tcsf.app.mapper.AppClientMapper;
import cn.topcodes.tcsf.app.mapper.AppUserMapper;
import cn.topcodes.tcsf.app.service.AppOperateLogService;
import cn.topcodes.tcsf.app.service.AppUserService;
import cn.topcodes.tcsf.app.service.DailyActiveUsersService;
import cn.topcodes.tcsf.app.service.impl.report.ReportServiceImpl;
import com.google.common.base.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import tk.mybatis.mapper.entity.Example;

import java.beans.PropertyEditor;
import java.beans.PropertyEditorManager;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Unicorn
 * @date 2017/5/5
 */
@Service("appUserService")
@Transactional(readOnly = true, rollbackFor = Exception.class)
public class AppUserServiceImpl extends GenericService<AppUser> implements AppUserService {

    @Autowired
    private AppUserMapper appUserMapper;

    @Autowired
    private AppUserParamServiceImpl appUserParamService;

    @Autowired
    private AppClientServiceImpl appClientService;

    @Autowired
    private AppClientMapper appClientMapper;

    @Autowired
    private DailyActiveUsersService dailyActiveUsersService;

    @Autowired
    private AppOperateLogService appOperateLogService;

    /**
     * 注册APP用户
     *
     * @param username 用户名
     * @param password 密码
     * @param realName 真实姓名
     * @return app用户对象
     */
    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public AppUser register(String username, String password, String realName) {
        Assert.hasText(username, "'username' cannot be blank");
        Assert.hasText(password, "'password' cannot be blank");
        Assert.isTrue(!this.exist(AppUser.FIELD_USERNAME, username), "'username' already exists");
        AppUser appUser = new AppUser();
        appUser.setUsername(username);
        appUser.setRole(AppUser.ROLE_NORMAL);
        appUser.setPassword(password);
        appUser.setRealName(realName);
        this.create(appUser);
        return appUser;
    }

    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public Long create(AppUser appUser) {
        //  TODO
//        appUser.setPassword(DigestUtil.md5(appUser.getPassword()).toUpperCase());
        return super.create(appUser);
    }

    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public String login(String username, String password) {
        AppUser appUser = this.validPassword(username, password);
        return appClientService.loginDefaultApp(appUser.getId());
    }

    /**
     * 登录并提交客户端信息
     *
     * @param username      用户名
     * @param password      密码
     * @param appClientInfo 客户端信息
     * @return token
     */
    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public String login(String username, String password, AppClientInfo appClientInfo) {
        AppUser appUser = this.validPassword(username, password);
        AppUserLoginEvent loginEvent = new AppUserLoginEvent();
        loginEvent.setAppClientInfo(appClientInfo);
        loginEvent.setUsername(username);
        loginEvent.setPassword(password);
        SpringContextHolder.publishEvent(loginEvent);
        return this.appClientService.login(appUser.getId(), appClientInfo);
    }

    /**
     * 验证用户名密码
     *
     * @param username 用户名
     * @param password 密码
     * @return 用户信息，用户名密码将抛出异常
     */
    @Override
    public AppUser validPassword(String username, String password) {
        Assert.hasText(username, "'username' cannot be blank");
        Assert.hasText(password, "'password' cannot be blank");
        Example example = this.buildExample();
        example.or().andEqualTo(AppUser.FIELD_USERNAME, username)
                .andEqualTo(AppUser.FIELD_PASSWORD, password);
        AppUser appUser = this.findOne(example);
        Assert.notNull(appUser, "'username' or 'password' is incorrect");
        return appUser;
    }

    /**
     * 获取指定app，用户的语言
     *
     * @param app       app
     * @param appUserId app用户id
     * @return 语言
     */
    @Override
    public String getLanguage(String app, Long appUserId) {
        AppClient appClient = this.appClientService.findBy(app, appUserId);
        return appClient != null ? appClient.getLanguage() : null;
    }

    /**
     * 获取默认app，用户的语言
     *
     * @param appUserId
     * @return
     */
    @Override
    public String getLanguage(Long appUserId) {
        return this.getLanguage(AppClient.APP_DEFAULT, appUserId);
    }

    /**
     * 设置默认app，用户的语言
     *
     * @param appUserId app用户id
     * @param language  语言
     */
    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void setLanguage(Long appUserId, String language) {
        this.setLanguage(AppClient.APP_DEFAULT, appUserId, language);
    }

    /**
     * 设置指定app，用户的语言
     *
     * @param app       app
     * @param appUserId app用户ID
     * @param language  语言
     */
    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void setLanguage(String app, Long appUserId, String language) {
        AppClient appClient = this.appClientService.findBy(app, appUserId);
        Assert.notNull(appClient, "The user is not logged in the application " + app);
        appClient.setLanguage(language);
        this.appClientService.save(appClient);
    }

    /**
     * 获取默认app，用户的APP版本信息
     *
     * @param appUserId app用户ID
     * @return app版本信息
     */
    @Override
    public String getAppVersion(Long appUserId) {
        return this.getAppVersion(AppClient.APP_DEFAULT, appUserId);
    }

    /**
     * 获取指定app，用户的app版本信息
     *
     * @param app       app
     * @param appUserId app用户ID
     * @return app版本信息
     */
    @Override
    public String getAppVersion(String app, Long appUserId) {
        AppClient appClient = this.appClientService.findBy(app, appUserId);
        return appClient != null ? appClient.getAppVersion() : null;
    }

    /**
     * 在默认app，设置用户的app版本信息
     *
     * @param appUserId  app用户ID
     * @param appVersion app版本信息
     */
    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void setAppVersion(Long appUserId, String appVersion) {
        this.setAppVersion(AppClient.APP_DEFAULT, appUserId, appVersion);
    }

    /**
     * 在指定app，设置用户的app版本信息
     *
     * @param app        app
     * @param appUserId  app用户ID
     * @param appVersion app版本信息
     */
    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void setAppVersion(String app, Long appUserId, String appVersion) {
        AppClient appClient = this.appClientService.findBy(app, appUserId);
        appClient.setAppVersion(appVersion);
        this.appClientService.save(appClient);
    }


    /**
     * 获取默认app，用户的终端信息
     *
     * @param appUserId app用户ID
     * @return 终端信息
     */
    @Override
    public String getTerminal(Long appUserId) {
        return this.getTerminal(AppClient.APP_DEFAULT, appUserId);
    }

    /**
     * 获取指定app，用户的终端信息
     *
     * @param app       app
     * @param appUserId app用户ID
     * @return 终端信息
     */
    @Override
    public String getTerminal(String app, Long appUserId) {
        AppClient appClient = this.appClientService.findBy(app, appUserId);
        return appClient != null ? appClient.getTerminal() : null;
    }

    /**
     * 在默认app，设置用户的终端信息
     *
     * @param appUserId app用户ID
     * @param terminal  终端信息
     */
    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void setTerminal(Long appUserId, String terminal) {
        this.setTerminal(AppClient.APP_DEFAULT, appUserId, terminal);
    }

    /**
     * 在指定app，设置用户的终端信息
     *
     * @param app       app
     * @param appUserId app用户ID
     * @param terminal  终端信息
     */
    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void setTerminal(String app, Long appUserId, String terminal) {
        AppClient appClient = this.appClientService.findBy(app, appUserId);
        appClient.setTerminal(terminal);
        this.appClientService.save(appClient);
    }

    /**
     * 获取默认app，用户的平台信息
     *
     * @param appUserId app用户ID
     * @return 平台信息
     */
    @Override
    public String getPlatform(Long appUserId) {
        return this.getPlatform(AppClient.APP_DEFAULT, appUserId);
    }

    /**
     * 获取指定app，用户的平台信息
     *
     * @param app       app
     * @param appUserId app用户ID
     * @return 平台信息
     */
    @Override
    public String getPlatform(String app, Long appUserId) {
        AppClient appClient = this.appClientService.findBy(app, appUserId);
        return appClient != null ? appClient.getPlatform() : null;
    }

    /**
     * 在默认app，设置用户的终端信息
     *
     * @param appUserId app用户ID
     * @param platform  平台信息
     */
    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void setPlatform(Long appUserId, String platform) {
        this.setPlatform(AppClient.APP_DEFAULT, appUserId, platform);
    }

    /**
     * 在指定app，设置用户的终端信息
     *
     * @param app       app
     * @param appUserId app用户ID
     * @param platform  平台信息
     */
    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void setPlatform(String app, Long appUserId, String platform) {
        AppClient appClient = this.appClientService.findBy(app, appUserId);
        appClient.setPlatform(platform);
        this.appClientService.save(appClient);
    }

    /**
     * 更新默认app，用户活跃时间
     *
     * @param appUserId app用户ID
     */
    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void active(Long appUserId) {
        this.active(AppClient.APP_DEFAULT, appUserId);
    }

    /**
     * 更新指定app，用户活跃时间
     *
     * @param app       app
     * @param appUserId app用户ID
     */
    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void active(String app, Long appUserId) {
        AppClient appClient = this.appClientService.findBy(app, appUserId);
        appClient.setLastActiveTime(new Date());

        dailyActiveUsersService.record(app, appUserId);

        this.appClientService.save(appClient);
    }

    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void logout(String token) {
        AppUser appUser = this.findByToken(token);
        if(appUser != null) {
            this.appClientService.logoutDefaultApp(token);
            appOperateLogService.record(appUser.getId(), "", AppOperateLog.TYPE_LOGOUT, "User logged out");

            AppUserLogoutEvent logoutEvent = new AppUserLogoutEvent();
            logoutEvent.setAppUserId(appUser.getId());
            logoutEvent.setApp(AppClient.APP_DEFAULT);
            SpringContextHolder.publishEvent(logoutEvent);
        }
    }

    /**
     * 退出指定app
     *
     * @param app   app
     * @param token 凭证
     */
    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void logout(String app, String token) {
        AppUser appUser = this.findByToken(token);
        if(appUser != null) {
            this.appClientService.logout(app, token);
            appOperateLogService.record(appUser.getId(), "", AppOperateLog.TYPE_LOGOUT, "User logged out");

            AppUserLogoutEvent logoutEvent = new AppUserLogoutEvent();
            logoutEvent.setAppUserId(appUser.getId());
            logoutEvent.setApp(app);
            SpringContextHolder.publishEvent(logoutEvent);
        }
    }

    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void modifyPassword(Long id, String oldPwd, String newPwd) {
        Assert.hasText(oldPwd, "The original password cannot be blank");
        Assert.hasText(newPwd, "The new password cannot be blank");
        AppUser appUser = this.findOneOrFail(id);
        if (!oldPwd.equals(appUser.getPassword())) {
            throw new IllegalArgumentException("Original password input error");
        }
        appUser.setPassword(newPwd);
        this.update(appUser);
    }

    /**
     * 重置密码
     *
     * @param id       app用户ID
     * @param password 新密码
     */
    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void resetPassword(Long id, String password) {
        AppUser appUser = this.findOneOrFail(id);
        appUser.setPassword(DigestUtil.md5(password).toUpperCase());
        this.update(appUser);
    }

    @Override
    public AppUser findByToken(String token) {
        if (StringUtil.isBlank(token)) {
            return null;
        }
        AppClient appClient = this.appClientService.findByToken(token);
        if (appClient == null) {
            return null;
        }
        return this.findOne(appClient.getAppUserId());
    }

    @Override
    public String findUsernameByToken(String token) {
        if (StringUtil.isBlank(token)) {
            return null;
        }
        AppUser appUser = this.findByToken(token);
        return appUser != null ? appUser.getUsername() : null;
    }

    /**
     * 是否在线
     *
     * @param token 凭证
     * @return true：在线；false:离线
     */
    @Override
    public boolean isOnline(String token) {
        return this.findByToken(token) != null;
    }

    @Override
    public List<String> findTokensByUsername(String username) {
        AppUser appUser = this.findByUsername(username);
        return this.appClientService.getDefaultAppTokens(appUser.getId());
    }

    /**
     * 根据用户名获取默认app的token
     *
     * @param username 用户名
     * @return 凭证
     */
    @Override
    public String findTokenByUsername(String username) {
        AppUser appUser = this.findByUsername(username);
        return this.appClientService.getDefaultAppToken(appUser.getId());
    }

    /**
     * 获取用户在指定app上的token
     *
     * @param app       app
     * @param appUserId app用户ID
     * @return 凭证
     */
    @Override
    public String getAppToken(String app, Long appUserId) {
        return this.appClientService.getAppToken(app, appUserId);
    }

    /**
     * 获取用户在指定app上的token列表
     *
     * @param app       app
     * @param appUserId app用户ID
     * @return 凭证列表
     */
    @Override
    public List<String> getAppTokens(String app, Long appUserId) {
        return this.appClientService.getAppTokens(app, appUserId);
    }

    /**
     * 通过用户名查找用户
     *
     * @param username 用户名
     * @return app用户信息对象
     */
    @Override
    public AppUser findByUsername(String username) {
        if (StringUtil.isBlank(username)) {
            return null;
        }
        Example example = this.buildExample();
        example.or().andEqualTo(AppUser.FIELD_USERNAME, username);
        return this.findOne(example);
    }

    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void updateAppUser(AppUser form) {
        Assert.notNull(form.getId(), "'appUserId' cannot be null");
        AppUser appUser = this.findOne(form.getId());
        Assert.notNull(appUser, "APP user does not exist");
        appUser.setRealName(form.getRealName());
        appUser.setGender(form.getGender());
        appUser.setTel(form.getTel());
        appUser.setEmail(form.getEmail());
        this.update(appUser);
    }

    /**
     * 删除用户参数
     * @param appUserId
     * @param name
     */
    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void deleteAppUserParam(Long appUserId, String name) {
        Assert.notNull(appUserId, "'appUserId' cannot be null");
        Assert.hasText(name, "'name' cannot be blank");
        Example example = this.appUserParamService.buildExample();
        example.or().andEqualTo(AppUserParam.FIELD_APP_USER_ID, appUserId)
                .andEqualTo(AppUserParam.FIELD_PROP_NAME, name);
        this.appUserParamService.deleteByExample(example);
    }

    /**
     * 设置用户参数
     *
     * @param appUserId app用户ID
     * @param name      参数名
     * @param value     参数值
     */
    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void setAppUserParam(Long appUserId, String name, Object value) {
        this.setAppUserParam(appUserId, name, value, USER_PARAM_VALUE_REPEAT_ALLOW);
    }

    /**
     * 设置用户参数
     *
     * @param appUserId        app用户ID
     * @param name             参数名
     * @param value            参数值
     * @param allowValueRepeat 是否允许取值重复，常量 USER_PARAM_VALUE_REPEAT_*
     */
    @Override
    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void setAppUserParam(Long appUserId, String name, Object value, String allowValueRepeat) {
        if (USER_PARAM_VALUE_REPEAT_ALLOW.equals(allowValueRepeat)) {
            setAppUserParamInternal(appUserId, name, value);
            return;
        }

        PropertyEditor editor = PropertyEditorManager.findEditor(value.getClass());
        if (editor == null) {
            throw new IllegalArgumentException("System Properties does not support data types: " + value.getClass().getName());
        }
        editor.setValue(value);

        //  如果已存在
        Example example = this.appUserParamService.buildExample();
        example.or().andEqualTo(AppUserParam.FIELD_PROP_NAME, name).andEqualTo(AppUserParam.FIELD_PROP_VALUE, editor.getAsText());

        if (USER_PARAM_VALUE_REPEAT_OVERRIDE.equals(allowValueRepeat)) {
            //  覆盖
            if (this.appUserParamService.exist(example)) {
                this.appUserParamService.deleteByExample(example);
            }
            setAppUserParamInternal(appUserId, name, value);
        }

        //  其他忽略
    }

    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public void setAppUserParamInternal(Long appUserId, String name, Object value) {
        AppUserParam param = this.appUserParamService.findByPropName(appUserId, name);
        if (value == null || value instanceof String || value instanceof Integer || value instanceof Long ||
                value instanceof Boolean || value instanceof Double) {
            if (value == null) {
                param.setPropValue(null);
            } else {
                PropertyEditor editor = PropertyEditorManager.findEditor(value.getClass());
                if (editor == null) {
                    throw new IllegalArgumentException("System Properties does not support data types: " + value.getClass().getName());
                }
                editor.setValue(value);
                param.setPropValue(editor.getAsText());
            }
            this.appUserParamService.save(param);
        } else {
            throw new IllegalArgumentException("User parameter only supports: Integer/Long/Float/Double/Boolean/String");
        }
    }

    /**
     * 获取用户参数
     *
     * @param appUserId    app用户ID
     * @param name         参数名
     * @param clazz        参数类型Class
     * @param defaultValue 默认值
     * @param <T>          参数类型，仅支持Integer、Long、Double、String、Boolean
     * @return 参数值
     */
    @Override
    public <T> T getAppUserParamValue(Long appUserId, String name, Class<T> clazz, T defaultValue) {
        AppUserParam param = this.appUserParamService.findByPropName(appUserId, name);
        if (Strings.isNullOrEmpty(param.getPropValue())) {
            return defaultValue;
        }
        PropertyEditor editor = PropertyEditorManager.findEditor(clazz);
        if (editor == null) {
            throw new IllegalArgumentException("System Properties does not support data types: " + clazz.getName());
        }
        editor.setAsText(param.getPropValue());
        Object obj = editor.getValue();
        if (obj == null) {
            return defaultValue;
        }
        return (T) obj;
    }

    /**
     * 新用户趋势图 查找指定用户
     *
     * @param searchParams
     * @param page
     * @param size
     * @return
     */
    @Override
    public Page<AppUser> findPageAndSearchDate(Map<String, String[]> searchParams, int page, int size) {
        searchParams = searchParams == null ? new HashMap() : searchParams;
        page = page < 1 ? 1 : page;
        size = size < 1 ? 1 : size;
        Date time = null;
        Date end = null;
        Example example = this.buildExample();
        //查询出所有符合时间等的记录数量，需要在添加设备类型
        String[] startDates = searchParams.get("startDate");
        String[] endDates = searchParams.get("endDate");
        String[] chartDates = searchParams.get("chartDate");
        String chartDate = chartDates[0];
        if (StringUtil.isNotBlank(chartDate)) {
            try {
                time = DateUtil.change0H0M0S(DateUtil.parse(chartDate, DateUtil.YYYY_MM_DD));
                end = DateUtil.change23H59M59S(DateUtil.parse(chartDate, DateUtil.YYYY_MM_DD));
            } catch (ParseException e) {
                e.printStackTrace();
            }
        } else {
            if (null != endDates && endDates.length != 0) {
                String endDate = endDates[0];
                String startDate = startDates[0];
                if (StringUtil.isNotBlank(startDate) && StringUtil.isNotBlank(endDate)) {
                    try {
                        time = DateUtil.change0H0M0S(DateUtil.parse(startDate, DateUtil.YYYY_MM_DD));
                        end = DateUtil.change23H59M59S(DateUtil.parse(endDate, DateUtil.YYYY_MM_DD));
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                } else {
                    time = DateUtil.change0H0M0S(oneMonthAgo());
                    end = new Date();
                }
            }
        }
        example.or()
                .andGreaterThan("createdTime", time)
                .andLessThan("createdTime", end);
        DynamicQueryUtil.convert((Map) searchParams, example);
        Page<AppUser> appUserPage = this.findPage(example, page, size);
        return appUserPage;
    }

    @Override
    public Page<AppUser> findPageAndSearchAccountOnline(Map<String, String[]> searchParams, int page, int size) {
        searchParams = searchParams == null ? new HashMap() : searchParams;
        page = page < 1 ? 1 : page;
        size = size < 1 ? 1 : size;
        Date time = null;
        Date end = null;
        Example example = this.buildExample();
        //查询出所有符合时间等的记录数量，需要在添加设备类型
        String[] startDates = searchParams.get("startDate");
        String[] endDates = searchParams.get("endDate");
        String[] types = searchParams.get("type");
        String[] apptypes = searchParams.get("appType");
        String type = types[0];
        if (null != endDates && endDates.length != 0 && null != startDates && startDates.length != 0) {
            String endDate = endDates[0];
            String startDate = startDates[0];
            if (StringUtil.isNotBlank(startDate) && StringUtil.isNotBlank(endDate)) {
                try {
                    time = DateUtil.change0H0M0S(DateUtil.parse(startDate, DateUtil.YYYY_MM_DD));
                    end = DateUtil.change23H59M59S(DateUtil.parse(endDate, DateUtil.YYYY_MM_DD));
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            } else {
                time = DateUtil.change0H0M0S(oneMonthAgo());
                end = new Date();
            }
        }
        List<Long> allOnlineAccounts = new ArrayList<>();
        if (StringUtil.isNotBlank(apptypes[0])) {
            allOnlineAccounts = appClientMapper.findAllOnlineAccountsByAppType(apptypes[0], time, end);
        } else {
            allOnlineAccounts = appClientMapper.findAllOnlineAccounts(time, end);
        }
        if (!allOnlineAccounts.isEmpty()) {
            if ("online".equals(type)) {
                example.or()
                        .andIn("id", allOnlineAccounts);
            } else {
                example.or()
                        .andNotIn("id", allOnlineAccounts);
            }
        } else {
            if ("online".equals(type)) {
                return new Page<AppUser>(new ArrayList<AppUser>(), 0, page, size);
            }
        }
        DynamicQueryUtil.convert((Map) searchParams, example);
        Page<AppUser> appUserPage = this.findPage(example, page, size);
        return appUserPage;
    }

    @Override
    public Page<AppUser> findPageAndSearchAccountActive(Map<String, String[]> searchParams, int page, int size) {
        searchParams = searchParams == null ? new HashMap() : searchParams;
        page = page < 1 ? 1 : page;
        size = size < 1 ? 1 : size;
        Date startTime = null;
        Date endTime = null;
        //查询出所有符合时间等的记录数量，需要在添加设备类型
        String[] startDates = searchParams.get("startDate");
        String[] endDates = searchParams.get("endDate");
        String[] chartDates = searchParams.get("chartDate");
        String[] dateTypes = searchParams.get("dateType");
        String chartDate = chartDates[0];
        String dateType = dateTypes[0];
        if (StringUtil.isNotBlank(chartDate)) {
            switch (dateType) {
                case "day":
                    try {
                        startTime = DateUtil.change0H0M0S(DateUtil.parse(chartDate, DateUtil.YYYY_MM_DD));
                        endTime = DateUtil.change23H59M59S(DateUtil.parse(chartDate, DateUtil.YYYY_MM_DD));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    break;
                case "month":
                    //按年统计的话 +1月
                    try {
                        startTime = DateUtil.parse(chartDate, "yyyy-MM");
                        endTime = DateUtil.change23H59M59S(getMonthLastDay(DateUtil.parse(chartDate, "yyyy-MM")));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    break;
                case "year":
                    //按年统计的话 +1年
                    try {
                        startTime = DateUtil.parse(chartDate, "yyyy");
                        endTime = DateUtil.change23H59M59S(getYearLastDay(DateUtil.parse(chartDate, "yyyy")));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    break;
            }

        } else {
            if (null != endDates && endDates.length != 0) {
                String endDate = endDates[0];
                String startDate = startDates[0];
                if (StringUtil.isNotBlank(startDate) && StringUtil.isNotBlank(endDate)) {
                    switch (dateType) {
                        case "day":
                            try {
                                startTime = DateUtil.change0H0M0S(DateUtil.parse(startDate, DateUtil.YYYY_MM_DD));
                                endTime = DateUtil.change23H59M59S(DateUtil.parse(endDate, DateUtil.YYYY_MM_DD));
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            break;
                        case "month":
                            //按年统计的话 +1月
                            try {
                                startTime = DateUtil.parse(startDate, "yyyy-MM");
                                endTime = DateUtil.parse(endDate, "yyyy-MM");
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            break;
                        case "year":
                            //按年统计的话 +1年
                            try {
                                startTime = DateUtil.parse(startDate, "yyyy");
                                endTime = DateUtil.parse(endDate, "yyyy");
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            break;
                        default:
                            break;
                    }
                }
            }
        }

        //查询出所有符合条件的用户
        List<AppUser> appUsers = this.appUserMapper.findActiveUsers(startTime, endTime, page, size);
        int total = this.appUserMapper.getFoundRows();
        return new Page<AppUser>(appUsers, total, page, size);
    }

    @Override
    public Page<RespAppUserInfo> findPageAndSearchAccountByApp(Map<String, String[]> searchParams, int page, int size) {
        searchParams = searchParams == null ? new HashMap() : searchParams;
        page = page < 1 ? 1 : page;
        size = size < 1 ? 1 : size;
        String[] appTypes = searchParams.get("appType");
        String[] dataTypes = searchParams.get("dataType");
        String[] params = searchParams.get("param");
        Example appExample = appClientService.buildExample();
        if (StringUtil.isNotBlank(params[0]) && StringUtil.isNotBlank(dataTypes[0])) {
            if (StringUtil.isNotBlank(appTypes[0])) {
                appExample.or()
                        .andEqualTo(AppClient.FIELD_APP, appTypes[0])
                        .andEqualTo(dataTypes[0], params[0]);
            } else {
                appExample.or()
                        .andEqualTo(dataTypes[0], params[0]);
            }
        } else {
            if (StringUtil.isNotBlank(appTypes[0])) {
                appExample.or()
                        .andEqualTo(AppClient.FIELD_APP, appTypes[0]);
            }
        }
        Page<AppClient> clientPage = appClientService.findPage(appExample, page, size);
        Page<RespAppUserInfo> respAppUserInfoPage = new Page<>();
        respAppUserInfoPage.setRows(appClient2Vo(clientPage.getRows()));
        respAppUserInfoPage.setPageNumber(clientPage.getPageNumber());
        respAppUserInfoPage.setPageSize(clientPage.getPageSize());
        respAppUserInfoPage.setPageTotal(clientPage.getPageTotal());
        respAppUserInfoPage.setTotal(clientPage.getTotal());
        return respAppUserInfoPage;
    }

    @Override
    public List<UserRetentionVo> getUserRetention(Map<String, String[]> searchParams) {
        List<UserRetentionVo> userRetentionVos = new ArrayList<>();
        String[] startDates = searchParams.get("startDate");
        String[] endDates = searchParams.get("endDate");
        Date time = null;
        Date end = null;
        Example usersExample = this.buildExample();
        if (StringUtil.isNotBlank(startDates[0]) && StringUtil.isNotBlank(endDates[0])) {
            try {
                time = DateUtil.change0H0M0S(DateUtil.parse(startDates[0], DateUtil.YYYY_MM_DD));
                end = DateUtil.change23H59M59S(DateUtil.parse(endDates[0], DateUtil.YYYY_MM_DD));
            } catch (ParseException e) {
                e.printStackTrace();
            }
            usersExample.or().andGreaterThan("createdTime", time)
                    .andLessThan("createdTime", end);
            List<AppUser> appUsers = this.findAll(usersExample);

            Map<String, Double> userretention = ReportServiceImpl.USERRETENTION;
            if (null != userretention) {
                UserRetentionVo userRetentionVo = new UserRetentionVo();
                userRetentionVo.setDate("Overview");
                userRetentionVo.setUsers(appUsers.size() + 0L);
                userRetentionVo.setTomorrow(userretention.get("days1"));
                userRetentionVo.setThreeDays(userretention.get("days3"));
                userRetentionVo.setWeek(userretention.get("days7"));
                userRetentionVo.setTenDays(userretention.get("days10"));
                userRetentionVo.setFifteenDays(userretention.get("days15"));
                userRetentionVo.setTwentyDays(userretention.get("days20"));
                userRetentionVo.setThirtyDays(userretention.get("days30"));
                userRetentionVos.add(userRetentionVo);
            }

            if (null != appUsers && !appUsers.isEmpty()) {
                while (time.getTime() <= end.getTime()) {
                    Date startTime = time;
                    Date endTime = setDay(time, 1);
                    //当天注册的用户
                    List<AppUser> appUserList = appUsers.stream()
                            .filter((e) -> e.getCreatedTime().getTime() >= DateUtil.change0H0M0S(startTime).getTime() && e.getCreatedTime().getTime() <= DateUtil.change23H59M59S(startTime).getTime())
                            .collect(Collectors.toList());
                    UserRetentionVo userRetentionVo = new UserRetentionVo();
                    userRetentionVo.setDate(DateUtil.format(time, DateUtil.YYYY_MM_DD));
                    userRetentionVo.setUsers(appUserList.size() + 0L);

                    if (setDay(startTime, 1).getTime() <= System.currentTimeMillis()) {
                        userRetentionVo.setTomorrow(getActiveUsers(appUserList, setDay(startTime, 1)));
                    } else {
                        userRetentionVo.setTomorrow(null);
                    }

                    if (setDay(startTime, 3).getTime() <= System.currentTimeMillis()) {
                        userRetentionVo.setThreeDays(getActiveUsers(appUserList, setDay(startTime, 3)));
                    } else {
                        userRetentionVo.setThreeDays(null);
                    }

                    if (setDay(startTime, 7).getTime() <= System.currentTimeMillis()) {
                        userRetentionVo.setWeek(getActiveUsers(appUserList, setDay(startTime, 7)));
                    } else {
                        userRetentionVo.setWeek(null);
                    }

                    if (setDay(startTime, 10).getTime() <= System.currentTimeMillis()) {
                        userRetentionVo.setTenDays(getActiveUsers(appUserList, setDay(startTime, 10)));
                    } else {
                        userRetentionVo.setTenDays(null);
                    }

                    if (setDay(startTime, 15).getTime() <= System.currentTimeMillis()) {
                        userRetentionVo.setFifteenDays(getActiveUsers(appUserList, setDay(startTime, 15)));
                    } else {
                        userRetentionVo.setFifteenDays(null);
                    }

                    if (setDay(startTime, 20).getTime() <= System.currentTimeMillis()) {
                        userRetentionVo.setTwentyDays(getActiveUsers(appUserList, setDay(startTime, 20)));
                    } else {
                        userRetentionVo.setTwentyDays(null);
                    }

                    if (setDay(startTime, 30).getTime() <= System.currentTimeMillis()) {
                        userRetentionVo.setThirtyDays(getActiveUsers(appUserList, setDay(startTime, 30)));
                    } else {
                        userRetentionVo.setThirtyDays(null);
                    }
                    time = endTime;
                    userRetentionVos.add(userRetentionVo);
                }
            }
            return userRetentionVos;
        }
        return null;
    }

    public Double getActiveUsers(List<AppUser> appUsers, Date startTime) {
        Example example = dailyActiveUsersService.buildExample();
        List<Long> userIds = CollectionUtil.getPropList(appUsers, "id");
        if (null != userIds && !userIds.isEmpty()) {
            example.or()
                    .andEqualTo(DailyActiveUsers.FIELD_DAU_DATE, DateUtil.format(startTime, DateUtil.YYYY_MM_DD))
                    .andIn(DailyActiveUsers.FIELD_APP_USER_ID, userIds);
            List<DailyActiveUsers> dailyActiveUsers = dailyActiveUsersService.findAll(example);
            long count = dailyActiveUsers.stream()
                    .filter((e) -> e.getDauDate().getTime() == DateUtil.change0H0M0S(startTime).getTime() && userIds.contains(e.getAppUserId()))
                    .count();
            DecimalFormat df = new DecimalFormat("#.00");
            return new BigDecimal((double) count / userIds.size()).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
        }
        return 0D;
    }

    public List<RespAppUserInfo> appClient2Vo(List<AppClient> appClients) {
        List<RespAppUserInfo> respAppUserInfos = new ArrayList<>();
        if (null != appClients && !appClients.isEmpty()) {
            for (AppClient appClient : appClients) {
                RespAppUserInfo respAppUserInfo = new RespAppUserInfo();
                Long appUserId = appClient.getAppUserId();
                AppUser appUser = this.findOne(appUserId);
                if (null != appUser) {
                    respAppUserInfo.setUsername(appUser.getUsername());
                    respAppUserInfo.setTel(appUser.getTel());
                    respAppUserInfo.setRealName(appUser.getRealName());
                    respAppUserInfo.setRole(appUser.getRole());
                    respAppUserInfo.setGender(appUser.getGender());
                    respAppUserInfo.setEmail(appUser.getEmail());
                }
                respAppUserInfo.setApp(appClient.getApp());
                respAppUserInfo.setAppVersion(appClient.getAppVersion());
                respAppUserInfo.setLastActiveTime(appClient.getLastActiveTime());
                respAppUserInfo.setPlatform(appClient.getPlatform());
                respAppUserInfo.setTerminal(appClient.getTerminal());
                respAppUserInfos.add(respAppUserInfo);
            }
        }
        return respAppUserInfos;
    }

    public Date oneMonthAgo() {
        Calendar c = Calendar.getInstance();
        c.setTime(new Date());
        c.set(Calendar.DATE, c.get(Calendar.DATE) - 30);
        return c.getTime();
    }

    public Date getMonthLastDay(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.MONTH, 1);
        calendar.set(Calendar.DAY_OF_MONTH, 0);
        return calendar.getTime();
    }

    public Date getYearLastDay(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);

        Calendar calendar = Calendar.getInstance();
        calendar.clear();
        calendar.set(Calendar.YEAR, cal.get(Calendar.YEAR));
        calendar.roll(Calendar.DAY_OF_YEAR, -1);
        return calendar.getTime();
    }

    /**
     * 设置天数 正数增加 负数减少 单位为天
     *
     * @param time
     * @param days
     * @return
     */
    public Date setDay(Date time, int days) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(time);
        calendar.add(Calendar.DAY_OF_YEAR, days);
        return calendar.getTime();
    }
}
